﻿Imports Microsoft.VisualStudio.TestTools.UnitTesting

Imports VBF.CompilerModel.Scanner
Imports VBF.CompilerModel.Scanner.RegularExpression
Imports System.IO
Imports System.Runtime.Serialization.Json


'''<summary>
'''This is a test class for DFAModelTest and is intended
'''to contain all DFAModelTest Unit Tests
'''</summary>
<TestClass()> _
Public Class DFAModelTest


    Private testContextInstance As TestContext

    '''<summary>
    '''Gets or sets the test context which provides
    '''information about and functionality for the current test run.
    '''</summary>
    Public Property TestContext() As TestContext
        Get
            Return testContextInstance
        End Get
        Set(ByVal value As TestContext)
            testContextInstance = value
        End Set
    End Property

#Region "Additional test attributes"
    '
    'You can use the following additional attributes as you write your tests:
    '
    'Use ClassInitialize to run code before running the first test in the class
    '<ClassInitialize()>  _
    'Public Shared Sub MyClassInitialize(ByVal testContext As TestContext)
    'End Sub
    '
    'Use ClassCleanup to run code after all tests in a class have run
    '<ClassCleanup()>  _
    'Public Shared Sub MyClassCleanup()
    'End Sub
    '
    'Use TestInitialize to run code before running each test
    '<TestInitialize()>  _
    'Public Sub MyTestInitialize()
    'End Sub
    '
    'Use TestCleanup to run code after each test has run
    '<TestCleanup()>  _
    'Public Sub MyTestCleanup()
    'End Sub
    '
#End Region


    '''<summary>
    '''A test for FromNFA
    '''</summary>
    <TestMethod()> _
    Public Sub FromNFATest()
        Dim E_IF = FromString("if")
        Dim E_ELSE = FromString("else")
        Dim E_ID = FromRange("a"c, "z"c).
            Sequence(
                (FromRange("a"c, "z"c) Or
                FromRange("0"c, "9"c)).Repeat
            )

        Dim E_NUM = FromRange("0"c, "9"c).Repeat1
        Dim E_ERROR = FromRange(CharRange.All)

        Dim lex As New Lexicon(Of String)

        Dim T_IF = lex.DefineToken(E_IF, "IF", 0)
        Dim T_ELSE = lex.DefineToken(E_ELSE, "ELSE", 0)
        Dim T_ID = lex.DefineToken(E_ID, "ID", 1)
        Dim T_NUM = lex.DefineToken(E_NUM, "NUM", 2)
        Dim T_ERROR = lex.DefineToken(E_ERROR, "ERROR", Integer.MaxValue)

        Dim seg = lex.CreateSegment(T_IF, T_ELSE, T_ID, T_NUM, T_ERROR)

        Dim N_Combine = seg.GenerateNFA()

        Dim D_Combine = DFAModel.FromNFA(N_Combine)

        D_Combine.Compress()

        Dim R_IF = D_Combine.SimulateRun("if")
        Assert.AreEqual("IF", lex.TokenTypes(R_IF))

        Dim R_ELSE = D_Combine.SimulateRun("else")
        Assert.AreEqual("ELSE", lex.TokenTypes(R_ELSE))

        Dim R_ID1 = D_Combine.SimulateRun("apple01")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID1))

        Dim R_ID2 = D_Combine.SimulateRun("if1")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID2))

        Dim R_ID3 = D_Combine.SimulateRun("else1")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID3))

        Dim R_ID4 = D_Combine.SimulateRun("hahaif")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID4))

        Dim R_ID5 = D_Combine.SimulateRun("haha01else")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID5))

        Dim R_ID6 = D_Combine.SimulateRun("ifelse")
        Assert.AreEqual("ID", lex.TokenTypes(R_ID6))

        Dim R_NUM1 = D_Combine.SimulateRun("123")
        Assert.AreEqual("NUM", lex.TokenTypes(R_NUM1))

        Dim R_NUM2 = D_Combine.SimulateRun("6758765876585876546754654")
        Assert.AreEqual("NUM", lex.TokenTypes(R_NUM2))

        Dim R_ERROR1 = D_Combine.SimulateRun("!")
        Assert.AreEqual("ERROR", lex.TokenTypes(R_ERROR1))

        Dim R_ERROR2 = D_Combine.SimulateRun("啊")
        Assert.AreEqual("ERROR", lex.TokenTypes(R_ERROR2))

        Dim R_Stopped = D_Combine.SimulateRun("sadf !@#$ asdf")
        Assert.AreEqual(Nothing, lex.TokenTypes(R_Stopped))

    End Sub

    <TestMethod()> _
    Public Sub DFAScannerEngine_SerializationTest()
        Dim quote = FromChar(""""c)
        Dim doubleQuote = quote.Sequence(quote)

        Dim lfIndex = AscW(vbLf) '10
        Dim crIndex = AscW(vbCr) '13
        Dim quoteIndex = AscW(""""c) '34

        Dim stringChars = FromRange(Char.MinValue, ChrW(lfIndex - 1)) Or
                          FromRange(ChrW(lfIndex + 1), ChrW(crIndex - 1)) Or
                          FromRange(ChrW(crIndex + 1), ChrW(quoteIndex - 1)) Or
                          FromRange(ChrW(quoteIndex + 1), Char.MaxValue)

        Dim E_STRING =
            quote.Sequence(
                (stringChars.Repeat.Sequence(doubleQuote.Optional.Sequence(stringChars.Repeat))).Repeat
            ).Sequence(quote)

        Dim lex As New Lexicon(Of String)

        Dim T_STRING = lex.DefineToken(E_STRING, "STRING")

        Dim seg = lex.CreateSegment(T_STRING)
        Dim N_STRING = seg.GenerateNFA()

        Dim D_STRING = DFAModel.FromNFA(N_STRING)
        D_STRING.Compress()

        Dim engine = D_STRING.GenerateScannerEngine()

        Dim serializer As New DataContractJsonSerializer(engine.GetType())

        Dim stream As New MemoryStream

        serializer.WriteObject(stream, engine)

        stream.Seek(0, SeekOrigin.Begin)

        Dim sr As New StreamReader(stream)
        Dim serializedString = sr.ReadToEnd

        stream.Seek(0, SeekOrigin.Begin)

        Dim engine2 = TryCast(serializer.ReadObject(stream), DFAScannerEngine)


        Dim trueCases =
            <cases>
                <case>"asdf"</case>
                <case>"asd  asdf   中文哦   f "</case>
                <case>"全中文"</case>
                <case>""</case>
                <case>""""</case>
                <case>"asdf"""</case>
                <case>"""asdf"</case>
                <case>""""""</case>
                <case>"asdf""""asdf"</case>
                <case>"""asdf""asdf"""</case>
                <case>"中文“""asdf""as”df"""</case>
            </cases>

        Dim falseCases =
            <cases>
                <case>"asdf</case>
                <case>asdf"</case>
                <case>"""</case>
                <case>"" "</case>
                <case>"asdf""</case>
                <case>""asdf"</case>
                <case>"asdf" "</case>
                <case>"as
                df"</case>
            </cases>

        For Each testcase In trueCases.<case>
            Dim r = TestRun(engine2, testcase.Value)

            Assert.AreEqual("STRING", lex.TokenTypes(r), testcase.Value)
        Next

        For Each testcase In falseCases.<case>
            Dim r = TestRun(engine2, testcase.Value)

            Assert.AreEqual(Nothing, lex.TokenTypes(r), testcase.Value)
        Next
    End Sub

    <TestMethod()> _
    Public Sub DFAScannerEngine_Stress()
        Dim two = FromChar("2"c)
        Dim twoStress As RegularExpression = two.Optional

        For i = 1 To 50
            twoStress = twoStress.Sequence(two.Optional)
        Next

        twoStress = twoStress.Sequence(FromString("222222222222222222222222222222222222222222222222222222222222"))

        Dim lex As New Lexicon(Of String)

        Dim T_TWO = lex.DefineToken(twoStress, "TWO")

        Dim seg = lex.CreateSegment(T_TWO)
        Dim N_TWO = seg.GenerateNFA()

        Dim D_TWO = DFAModel.FromNFA(N_TWO)
        D_TWO.Compress()

        Dim engine = D_TWO.GenerateScannerEngine()

        Dim test = "222222222222222222222222222222222222222222222222222222222222"

        Dim r = TestRun(engine, test)

        Assert.AreEqual("TWO", lex.TokenTypes(r))
    End Sub

    Private Shared Function TestRun(ByVal engine As DFAScannerEngine, ByVal input As String) As Integer
        engine.Reset()

        For i = 0 To input.Length - 1
            engine.Input(input(i))
        Next

        Return engine.GetCurrentTokenTypeIndex
    End Function
End Class